#include "mcitemgeometry.h"

#include <QPainter>
#include <QDebug>

#include <QImage>
#include <QVector3D>
#include <QQmlContext>
#include <QQmlComponent>
#include <QQmlFile>
#include <QFile>

MCItemGeometry::MCItemGeometry(QQuick3DObject *parent)
    : QQuick3DGeometry(parent)
{

}

MCItemGeometry::~MCItemGeometry()
{
}

void MCItemGeometry::setSource(QString source)
{
    if(m_source != source)
    {
        m_source = source;
        update();
        sourceChanged(m_source);
    }


}

QString MCItemGeometry::source()
{
    return m_source;
}

static int getAlphaData(const QImage& src,int x,int y){
    if(x < 0 || y < 0||x >= src.width() || y >= src.height())
    {
        return 0;
    }


    return src.pixelColor(x,y).alpha();
}

static void pushVertix(const QVector3D& ver,const QVector3D& normal,const QVector2D& uv,QByteArray& target){
    float vertice_values[3] = {ver.x(),ver.y(),ver.z()};
    target.append((const char*)vertice_values,sizeof(vertice_values));

    float normal_values[3] = {normal.x(),normal.y(),normal.z()};
    target.append((const char*)normal_values,sizeof(normal_values));

    float uv_values[2] = {uv.x(),uv.y()};
    target.append((const char*)uv_values,sizeof(uv_values));
}

static void genTraingle(QVector3D* verArr,const QVector2D & uv,QByteArray & target){
    QVector3D v1 = verArr[0] - verArr[1];
    QVector3D v2 = verArr[0] - verArr[2];

    QVector3D normal = QVector3D::crossProduct(v1,v2).normalized();
    pushVertix(verArr[0],normal,uv,target);
    pushVertix(verArr[1],normal,uv,target);
    pushVertix(verArr[2],normal,uv,target);
}

//生成平面
static void genPlain(QVector3D* verArr,const QVector2D & uv,QByteArray & target){
    QVector3D f1[3] = {verArr[0],verArr[3],verArr[2]};
    genTraingle(f1,uv,target);

    QVector3D f2[3] = {verArr[0],verArr[2],verArr[1]};
    genTraingle(f2,uv,target);
}

enum Direction{
    Left = 1,//x-
    Right = 0b10,//x+
    Up = 0b100,//y+
    Down = 0b1000,//y-
    Front = 0b10000,//z+
    Behind = 0b100000,//z-
    All = 0xffffffff
};

//生成一个方盒子
static QByteArray genBox(QVector3D size,QVector3D center,const QVector2D uv,Direction direction){

    QByteArray ret;
    QVector3D vertex[8];//方盒子对应的8点
    vertex[0] = {size[0] / 2,size[1] / 2,size[2] / 2};
    vertex[1] = {size[0] / 2,size[1] / 2,-size[2] / 2};
    vertex[2] = {size[0] / 2,-size[1] / 2,size[2] / 2};
    vertex[3] = {size[0] / 2,-size[1] / 2,-size[2] / 2};
    vertex[4] = {-size[0] / 2,size[1] / 2,size[2] / 2};
    vertex[5] = {-size[0] / 2,size[1] / 2,-size[2] / 2};
    vertex[6] = {-size[0] / 2,-size[1] / 2,size[2] / 2};
    vertex[7] = {-size[0] / 2,-size[1] / 2,-size[2] / 2};

    for(auto &i:vertex)
    {
        i += center;
    }

    if(direction & Left)
    {
        QVector3D plain[] = {vertex[4],vertex[6],vertex[7],vertex[5]};
        genPlain(plain,uv,ret);
    }

    if(direction & Right)
    {
        QVector3D plain[] = {vertex[0],vertex[1],vertex[3],vertex[2]};
        genPlain(plain,uv,ret);
    }

    if(direction & Up)
    {
        QVector3D plain[] = {vertex[0],vertex[4],vertex[5],vertex[1]};
        genPlain(plain,uv,ret);
    }

    if(direction & Down)
    {
        QVector3D plain[] = {vertex[2],vertex[3],vertex[7],vertex[6]};
        genPlain(plain,uv,ret);
    }

    if(direction & Front)
    {
        QVector3D plain[] = {vertex[0],vertex[2],vertex[6],vertex[4]};
        genPlain(plain,uv,ret);
    }

    if(direction & Behind)
    {
        QVector3D plain[] = {vertex[1],vertex[5],vertex[7],vertex[3]};
        genPlain(plain,uv,ret);
    }

    return ret;
}

static QByteArray buildVertex(const QImage& src,QVector3D pixSize){
    QByteArray ret;
    int width = src.width(),height = src.height();

    QVector3D origin;
    origin[0] = - pixSize[0] * (width / 2 - 0.5);
    origin[1] = 0;
    origin[2] = - pixSize[2] * (height / 2 - 0.5);

    QVector2D uvDelta(1/width / 2,- 1/height / 2);

    for(auto i = 0;i< width;i++)
    {
        for(auto j = 0;j < height;j++)
        {

            bool center = getAlphaData(src,i,j);

            bool left = getAlphaData(src,i - 1,j);
            bool right = getAlphaData(src,i + 1,j);
            bool up = getAlphaData(src,i,j - 1);
            bool down = getAlphaData(src,i,j + 1);

            if(!center){
                continue;
            }

            int dir = Direction::Up | Direction::Down;

            if(!left)
            {
                dir |= Direction::Left;
            }

            if(!right)
            {
                dir |= Direction::Right;
            }

            if(!up)
            {
                dir |= Direction::Behind;
            }

            if(!down)
            {
                dir |= Direction::Front;
            }

            QVector3D pos = origin + QVector3D{pixSize[0] * i,0,pixSize[2] * j};

            QVector2D uv(1/(float)width * i,1 -1/(float)height * j);
            uv += uvDelta;

            ret.append(genBox(pixSize,pos,uv,(Direction)dir));
        }
    }

    return ret;
}

void MCItemGeometry::update()
{
    const QSizeF baseSize = {12.5,12.5};

    clear();
    auto ctx = qmlContext(this);
    QString url = QQmlFile::urlToLocalFileOrQrc(ctx->resolvedUrl(m_source));
    QFile f(url);
    QImage src(url);
    if(src.isNull()) return;
    int width = src.width(),height = src.height();
    QSizeF pixSize = {baseSize.width() / width,baseSize.height() / height};
    float thick = pixSize.width();
    int stride = 3 * sizeof(float) + 3 * sizeof(float) + 2 * sizeof(float);


    QByteArray vertexData;
    vertexData = buildVertex(src,{(float)pixSize.width(),thick,(float)pixSize.height()});

    setVertexData(vertexData);
    setStride(stride);
    QVector3D min,max;
    min[0] = -baseSize.width() / 2;
    min[1] = -thick / 2;
    min[2] = -baseSize.height() / 2;
    max[0] = baseSize.width() / 2;
    max[1] = thick / 2;
    max[2] = baseSize.height() / 2;

    setBounds(min,max);
    setPrimitiveType(QQuick3DGeometry::PrimitiveType::Triangles);
    addAttribute(QQuick3DGeometry::Attribute::PositionSemantic,0,QQuick3DGeometry::Attribute::F32Type);
    addAttribute(QQuick3DGeometry::Attribute::NormalSemantic,3 * sizeof(float),QQuick3DGeometry::Attribute::F32Type);
    addAttribute(QQuick3DGeometry::Attribute::TexCoordSemantic,6 * sizeof(float),QQuick3DGeometry::Attribute::F32Type);
}
